package com.wonders.ghy.juc.模拟接口多线程;

import java.util.concurrent.*;

/**
 * 模拟一个接口，中有两个耗时操作 a任务和b任务 a任务需要3秒 b任务需要5秒
 * 当a和b任务全部执行成功后会返回两个值 假如a任务返回1 b任务返回2 当ab两个任务全部完成
 * 才计算出a和b返回的值的总和  这里线程模拟Spring框架使用线程池的方式来做模拟
 */
public class InterfaceThreadAsync {
    public static void main(String[] args) throws Exception{
        /**
         * 定义一个线程计数器 线程总和为2 存放a b两个线程 ，每当其中一个线程执行完成，
         * 计数器减1 直到计数器为0后才允许await()方法后面代码执行
         */
        CountDownLatch countDownLatch = new CountDownLatch(2);
        // 采用单例模式懒汉式加载 模拟spring中单例的线程池
        ThreadPoolExecutor threadPoolExecutor = SingletonThreadPool.getInstance();
        // 记录开始执行时的时间戳
        Long start = System.currentTimeMillis();
        try{
            FutureTask<Integer> aFutureTask = new FutureTask(()->{
                Integer status = null;
                try {
                    // 模拟a任务耗时操作延迟3秒
                    TimeUnit.SECONDS.sleep(3);
                    status=1;
                    System.out.println("a任务成功完成 耗时3秒");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                countDownLatch.countDown();
                return status;
            });
            FutureTask<Integer> bFutureTask = new FutureTask(()->{
                Integer status = null;
                try {
                    // 模拟b任务耗时操作延迟5秒
                    TimeUnit.SECONDS.sleep(5);
                    status=2;
                    System.out.println("b方法成功耗时5秒");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                countDownLatch.countDown();
                return status;

            });
            threadPoolExecutor.execute(aFutureTask);
            threadPoolExecutor.execute(bFutureTask);
            // 得到a线程执行完成后返回的结果
            Integer aResult = aFutureTask.get();
            // 得到b线程执行完成后返回的结果
            Integer bResult = bFutureTask.get();
            // 一直等待线程计数器的计数为0 为0的时候这里才释放 开始向下执行
            countDownLatch.await();
            System.out.println("ab方法全部执行完毕 开始计算ab方法返回的值总和");
            System.out.println(aResult+bResult);
        }finally {
            threadPoolExecutor.shutdown();
        }
        Long end = System.currentTimeMillis();
        System.out.println("总耗时"+(end - start) / 1000+"秒");
    }


}
class SingletonThreadPool {
    // 使用volatile保证可见性
    private static volatile ThreadPoolExecutor tpe = null;
    public SingletonThreadPool(){
    }
    public static ThreadPoolExecutor getInstance(){
        // 这里采用双端检测机制
        if (tpe == null) {
            synchronized (SingletonThreadPool.class){
                if (tpe == null) {
                    tpe=new ThreadPoolExecutor(
                            2,//核心线程数
                            5,//最大线程数
                            2L,//保持存活时间
                            TimeUnit.SECONDS,//保持存活时间单位
                            new LinkedBlockingQueue<>(3),//阻塞队列大小
                            Executors.defaultThreadFactory(),//默认线程工厂
                            // 中止策略：默认的，直接抛出RejectedExecutionException异常阻止系统正常运行
                            //new ThreadPoolExecutor.AbortPolicy());
                            // 呼叫者策略：该策略既不会抛弃任务，也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量。当前线程池无法执行时回退给调用这个线程的线程去执行
                            //new ThreadPoolExecutor.CallerRunsPolicy());
                            // 丢弃策略：DiscardPolicy:该策略默默地丢弃无法处理的任务，不予任何处理也不抛出异常。如果允许任务丢失，这是最好的一种策略。
                            //new ThreadPoolExecutor.DiscardPolicy());
                            // 丢弃最老策略：丢弃队列中等待最久的任务，然后把当前任务加入队列中尝试再次提交当前任务
                            new ThreadPoolExecutor.DiscardOldestPolicy());
                }
            }
        }
        return tpe;
    }
}

